package com.youth.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.youth.entity.PageBean;
import com.youth.entity.po.Commodity;
import com.youth.entity.vo.CommodityVO;
import com.youth.executor.AddByExcelThread;
import com.youth.mapper.ICommodityMaper;
import com.youth.mq.producer.RMQProducer;
import com.youth.result.StatusEnum;
import com.youth.utils.CommonUtil;
import com.youth.utils.ExcelReaderUtil;
import com.youth.utils.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName CommodityService
 * @Description: TODO
 * @Author yjwu
 * @Date 2019/10/30 23:11
 **/
@Service
@CacheConfig(cacheNames = "commodity")
@Slf4j
public class CommodityService {
    @Value("${seckillRMQExchangeName}")
    private String seckillRMQExchangeName;
    @Value("${seckillRMQQueueName}")
    private String seckillRMQQueueName;
    @Value("${seckillRMQRoutingKey}")
    private String seckillRMQRoutingKey;

    @Autowired
    private ICommodityMaper commodityMaper;
    @Autowired
    private RMQProducer rmqProducer;
    @Autowired
    @Qualifier(value = "objRedisTemplate")
    private RedisTemplate redisTemplate;
    @Value("${file.savePath}")
    private String savePath;
    @Value("${commodityRedisKeyPrefixName}")
    private String commodityRedisKeyPrefixName;
    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private List<String> excelHeaderlList = new ArrayList<>();

    @PostConstruct
    public void init() {
        excelHeaderlList.add("name");
        excelHeaderlList.add("typeThreeId");
        excelHeaderlList.add("headline");
        excelHeaderlList.add("placeAreaId");
        excelHeaderlList.add("doHot");
        excelHeaderlList.add("keywords");
        excelHeaderlList.add("price");
        excelHeaderlList.add("storageNumber");
        excelHeaderlList.add("soldNumber");
        excelHeaderlList.add("description");
        excelHeaderlList.add("remark");
    }

    /**
     * @MethodName: add
     * @Description: TODO 商品添加方法
     * @Param: * @param commodity
     * @Return: {@link com.youth.entity.po.Commodity}
     * @Author: yjwu
     * @Date: 2019/11/11 23:30
     **/
    @Transactional
    public CommodityVO add(CommodityVO commodityVO) throws Exception {
        try {
            Commodity commodity = CommonUtil.commodityVoToPo(commodityVO);
            commodityMaper.add(commodity);
            redisTemplate.opsForValue().set(commodityRedisKeyPrefixName + commodity.getCode(), commodityVO);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        } finally {
            return commodityVO;
        }
    }

    /**
     * @MethodName: addByExcel
     * @Description: TODO excel添加商品
     * @Param: * @param excel
     * @Return: {@link Integer}
     * @Author: yjwu
     * @Date: 2020/1/6 10:34
     **/
    @Transactional
    public Integer addByExcel(MultipartFile excel) throws Exception {
        AtomicInteger atomicInteger = new AtomicInteger(0);
        try {
            log.info("进入方法" + new Date());
            String beanName = "commodity";
            String savedFilePath = FileUtil.saveFile(excel, savePath);

            //1 根据bean处理excel列头数据
            boolean flag = ExcelReaderUtil.doFieldListOk(beanName, excelHeaderlList);
            if (flag) {
                //2 从excel文件中读取到数据，并转换为java对象
                List<Commodity> resultList = ExcelReaderUtil.getDataFromExcelFile(beanName, savedFilePath, excelHeaderlList, threadPoolTaskExecutor, Commodity.class);
                log.info("读取转换完毕" + new Date());
                AtomicInteger allNumber = new AtomicInteger(resultList.size());
                //2 处理线程数，每个线程需要处理的数据数量，计数器
                int threadNumbeer = 4;
                int numberOneThread = allNumber.get() / threadNumbeer;
                CountDownLatch countDownLatch = new CountDownLatch(threadNumbeer);
                //3 将1中返回的结果分割为多个list，供每个线程处理
                List<List<Commodity>> lists = ExcelReaderUtil.splitList(resultList, numberOneThread);
                //4 存放线程处理结果future
                List<Future> futureList = new ArrayList<>(threadNumbeer);
                //多线程处理3中返回的结果。
                log.info("开始添加" + new Date() + ",,,threadPoolTaskExecutor.getActiveCount()=" + threadPoolTaskExecutor.getActiveCount());

                for (int i = 0; i < threadNumbeer; i++) {
                    AddByExcelThread addByExcelThread = new AddByExcelThread(lists.get(i), countDownLatch);
                    Future<Object> future = threadPoolTaskExecutor.submit(addByExcelThread);
                    futureList.add(future);
                }
                countDownLatch.await();
                log.info("结束添加" + new Date());
                for (Future future :
                        futureList) {
                    Integer integer = (Integer) future.get();
                    log.info("future.get() {}" + integer);
                    atomicInteger.getAndAdd(integer);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        } finally {
            return atomicInteger.get();
        }
    }


    /**
     * @MethodName: delete
     * @Description: TODO 删除方法
     * @Param: * @param code 商品code
     * @Return: {@link Integer}
     * @Author: yjwu
     * @Date: 2020/1/6 10:38
     **/
    @Transactional
    public Integer delete(String code) {
        Integer delete = null;
        try {
            delete = commodityMaper.delete(code, StatusEnum.NOTINUSE.getStatus(), new Date());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return delete;
        }
    }

    /**
     * @MethodName: edit
     * @Description: TODO 修改
     * @Param: * @param commodityVO
     * @Return: {@link Integer}
     * @Author: yjwu
     * @Date: 2020/1/6 10:43
     **/
    @Transactional
    public Integer edit(CommodityVO commodityVO) {
        Integer edit = null;
        try {
            edit = commodityMaper.edit(CommonUtil.commodityVoToPo(commodityVO));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("commodity edit error");
        } finally {
            return edit;
        }
    }

    /**
     * @param soldNumber 需要减少的数量
     * @MethodName: decreaseStockNumber
     * @Description: TODO 删减库存
     * @Param: * @param code 商品code
     * @Return: {@link Integer}
     * @Author: yjwu
     * @Date: 2020/1/6 10:44
     **/
    @Transactional
    public Integer decreaseStockNumber(String code, int soldNumber) throws Exception {
        Integer result = null;
        CommodityVO commodityVO;
        try {
            if (redisTemplate.hasKey(commodityRedisKeyPrefixName + code)) {
                Object o = redisTemplate.opsForValue().get(commodityRedisKeyPrefixName + code);
                String s = JSONObject.toJSONString(o);
                commodityVO = JSONObject.toJavaObject(JSON.parseObject(s), CommodityVO.class);
            } else {
                Commodity commodity = commodityMaper.findByCode(code);
                commodityVO = CommonUtil.commodityPoToVo(commodity);
            }
            int storageNumber = commodityVO.getStorageNumber() - soldNumber;
            if (storageNumber >= 0) {
                commodityVO.setStorageNumber(storageNumber);
                commodityVO.setSoldNumber(soldNumber);
                redisTemplate.opsForValue().set(commodityRedisKeyPrefixName + code, commodityVO);
                result = commodityMaper.decreaseStockNumber(code, storageNumber, soldNumber, new Date());
            }
            rmqProducer.send(commodityRedisKeyPrefixName + code, seckillRMQExchangeName, seckillRMQRoutingKey);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        } finally {
            return result;
        }
    }


    /**
     * @MethodName: findByCode
     * @Description: TODO 根据商品code查询
     * @Param: * @param code
     * @Return: {@link Commodity}
     * @Author: yjwu
     * @Date: 2019/11/11 23:30
     **/
    public CommodityVO findByCode(String code) {
        CommodityVO commodityVO = null;
        Commodity commodity = null;
        try {
            if (redisTemplate.hasKey(commodityRedisKeyPrefixName + code)) {
                Object o = redisTemplate.opsForValue().get(commodityRedisKeyPrefixName + code);
                commodityVO = JSONObject.toJavaObject(JSONObject.parseObject(JSONObject.toJSONString(o)), CommodityVO.class);
            } else {
                commodity = commodityMaper.findByCode(code);
                commodityVO = CommonUtil.commodityPoToVo(commodity);
                redisTemplate.opsForValue().set(commodityRedisKeyPrefixName + code, commodityVO);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        } finally {
            return commodityVO;
        }

    }


    public CommodityVO findWithConditions(PageBean pageBean) throws Exception {
        CommodityVO commodityVO = null;
        try {
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        } finally {
            return commodityVO;
        }

    }
}